package autohotkeyide.autocompletion;

import autohotkeyide.IDETextField;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.fife.ui.autocomplete.AutoCompletion;
import org.fife.ui.autocomplete.DefaultCompletionProvider;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * @author sad
 */
public class AutoCompletionManager {

    private DefaultCompletionProvider provider;
    private ArrayList<VariableCompletion> systemVariables = new ArrayList<VariableCompletion>(100);
    private ArrayList<CommandCompletion> systemCommands = new ArrayList<CommandCompletion>(100);
    private IDETextField field;

    public AutoCompletionManager(IDETextField field) {
        this.field = field;
        provider = new DefaultCompletionProvider();
        loadCompletionInfo();

        AutoCompletion cp = new AutoCompletion(provider) {
            @Override
            protected int refreshPopupWindow() {
                beforePopup();
                return super.refreshPopupWindow();
            }
        };

        cp.setShowDescWindow(true);
        cp.install(field);
    }
    
    public ArrayList<CommandCompletion>getSystemFunctionsList(){
        return systemCommands;
    }
    public ArrayList<VariableCompletion>getSystemVariablesList(){
        return systemVariables;
    }

    private void loadCompletionInfo() {
        InputStream stream = AutoCompletionManager.class.getResourceAsStream("/resources/commandHelp.xml");
        if (stream == null) {
            throw new RuntimeException("Cannot find /resources/commandHelp.xml in resources");
        }
        try {
            Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(stream);
            loadCommands(doc);
            loadVariables(doc);
        } catch (ParserConfigurationException ex) {
            Logger.getLogger(AutoCompletionManager.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SAXException ex) {
            Logger.getLogger(AutoCompletionManager.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(AutoCompletionManager.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void loadCommands(Document document) {
        systemCommands.clear();
        NodeList nodes = document.getElementsByTagName("command");
        for (int i = 0; i < nodes.getLength(); i++) {
            Element commandTag = (Element) nodes.item(i);
            String commandName = commandTag.getAttribute("name");
            String help = commandTag.getElementsByTagName("description").item(0).getTextContent().trim();
            CommandCompletion command = new CommandCompletion(provider, commandName, help);
            systemCommands.add(command);
        }
    }

    private void loadVariables(Document document) {
        systemVariables.clear();
        NodeList nodes = document.getElementsByTagName("var");
        for (int i = 0; i < nodes.getLength(); i++) {
            Element variableTag = (Element) nodes.item(i);
            String varName = variableTag.getAttribute("name");
            String help = variableTag.getElementsByTagName("description").item(0).getTextContent().trim();
            VariableCompletion command = new VariableCompletion(provider, varName, help);
            systemVariables.add(command);
        }
    }
    private Pattern variablePattern = Pattern.compile("^\\s*([a-zA-Z_0-9]+)\\s*:?=.*$");

    private String checkIfCreationVar(String command) {
        Matcher matcher = variablePattern.matcher(command);
        if (matcher.matches()) {
            return matcher.group(1);
        }
        return null;
    }
    private Pattern functionPattern = Pattern.compile("^\\s*([a-zA-Z_0-9]+)\\s*\\((.*?)\\)\\s*\\{$");

    private String[] checkIfCreationFunction(String command) {
        Matcher matcher = functionPattern.matcher(command);
        if (matcher.matches()) {
            String[] res = new String[2];
            res[0] = matcher.group(1);
            if (matcher.groupCount() == 2) {
                res[1] = matcher.group(2);
            }
            return res;
        }
        return null;
    }

    public ArrayList<VariableCompletion> collectDynamicVariables() {
        ArrayList<VariableCompletion> list = new ArrayList<VariableCompletion>();
        String[] rows = field.getRowsBeforeAndSelectedRows();
        for (String row : rows) {
            String varName = checkIfCreationVar(row);
            if (varName != null) {
                list.add(new VariableCompletion(provider, varName, "Dynamic variable"));
            }
        }
        return list;
    }

    public ArrayList<CommandCompletion> collectDynamicFunctions() {
        ArrayList<CommandCompletion> list = new ArrayList<CommandCompletion>();
        String[] rows = field.getRowsBeforeAndSelectedRows();
        for (String row : rows) {
            String[] function = checkIfCreationFunction(row);
            if (function != null) {
                list.add(new CommandCompletion(provider, function[0], function[1] == null ? "No arguments" : function[1]));
            }
        }
        return list;
    }

    private void beforePopup() {
        provider.clear();
        String alreadyInsertedText = provider.getAlreadyEnteredText(field);
        String initText = field.getStringBeforeCaretAndStartOfRow();
        if (initText.endsWith(alreadyInsertedText)) {
            initText = initText.substring(0, initText.length() - alreadyInsertedText.length());
        }

        if (initText.trim().isEmpty()) {
            for (CommandCompletion command : systemCommands) {
                provider.addCompletion(command);
            }
        }
        for (VariableCompletion command : systemVariables) {
            provider.addCompletion(command);
        }

        for (VariableCompletion var : collectDynamicVariables()) {
            provider.addCompletion(var);
        }

        for (CommandCompletion command : collectDynamicFunctions()) {
            provider.addCompletion(command);
        }
    }
}
